/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *            Copyright (C) 2018 Institute of Computing Technology, CAS
 *               Author : Han Shukai (email : hanshukai@ict.ac.cn)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *         The kernel's entry, where most of the initialization work is done.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * */

#include <common.h>
#include <os/irq.h>
#include <os/mm.h>
#include <os/sched.h>
#include <screen.h>
#include <sbi.h>
#include <stdio.h>
#include <os/time.h>
#include <os/syscall.h>
#include <os/futex.h>
#include <test.h>

#include <csr.h>

extern void ret_from_exception();
extern void printk_task1(void);
extern void __global_pointer$();
list_node_t ready_list;

static void init_pcb_stack(
    ptr_t kernel_stack, ptr_t user_stack, ptr_t entry_point,
    pcb_t *pcb)
{
    regs_context_t *pt_regs =
        (regs_context_t *)(kernel_stack - sizeof(regs_context_t));
    regs_context_t *st_regs =
        (switchto_context_t *)(kernel_stack - sizeof(regs_context_t)-sizeof(switchto_context_t));
        st_regs->regs[0]=entry_point;
        st_regs->regs[1]=kernel_stack;
        st_regs->regs[2]=0;
        st_regs->regs[3]=0;
        st_regs->regs[4]=0;
        st_regs->regs[5]=0;
        st_regs->regs[6]=0;
        st_regs->regs[7]=0;
        st_regs->regs[8]=0;
        st_regs->regs[9]=0;
        st_regs->regs[10]=0;
        st_regs->regs[11]=0;
        st_regs->regs[12]=0;
        st_regs->regs[13]=0;
    /* TODO: initialization registers
     * note: sp, gp, ra, sepc, sstatus
     * gp should be __global_pointer$
     * To run the task in user mode,
     * you should set corresponding bits of sstatus(SPP, SPIE, etc.).
     */

    // set sp to simulate return from switch_to
    /* TODO: you should prepare a stack, and push some values to
     * simulate a pcb context.
     */
}

static void init_pcb()
{
     /* initialize all of your pcb and add them into ready_queue
     * TODO:
     */
    //pcb[NUM_MAX_TASK]是全局变量，初始化主要是要将pcb的kerne_stack和user_stack计算出来并在内存中分配。除此之外，
    //pcb数组和sched1_tasks和lock_tasks数组对应，任务数组是task_info结构体为元素，每个元素有entry_point和task_type两个域
    //entry_point是非常重要的域，指示进入此进程后指令的开始执行位置。初始化时就是进程代码的初始地址，如果发生过中断就是上次执行地址
    //这个地址会存在kernel栈的ra寄存器偏移量对应地址，调度到此进程后会从内存中将之拷贝到cpu的ra寄存器，然后jr ra跳转到该位置.
	init_list_head(&ready_queue);
	int i;
	for (i=0;i<num_sched1_tasks;i++){//num_sched1_task1为3而num_lock_tasks为2.进程1、2输出在屏幕的头两行，打印一段话并
			pcb[i].pid =process_id;  //输出执行次数。进程3是飞机。进程4和进程5完全一样，包含初始化lock,调度一次,打印等待语句获得lock
			process_id+=1;           //不断调度20次并打印正在使用lock的语句以及计数,释放lock并打印释放语句,调度。
                        //最终理想的结果是在屏幕的3、4行打印语句，两者语句永远不能相同。基本为一方打印Has acquired lock and running
			pcb[i].type=KERNEL_THREAD;  //和循环次数另一方为Applying for a lock。到20次时一方exit另一方放回ready_queue并且running
			pcb[i].status=TASK_READY;
			pcb[i].preempt_count=0;
			pcb[i].kernel_sp=allocPage(1);
			pcb[i].user_sp=allocPage(1);
			init_pcb_stack(pcb[i].kernel_sp,pcb[i].user_sp,sched1_tasks[i]->entry_point,&pcb[i]);
			list_add_tail(&pcb[i].list, &ready_queue);

	}
    for(;i<num_sched1_tasks+num_lock_tasks;i++){
            pcb[i].pid =process_id;
            process_id+=1;
            pcb[i].type=KERNEL_THREAD;
            pcb[i].status=TASK_READY;
            pcb[i].preempt_count=0;
            pcb[i].kernel_sp=allocPage(1);
            pcb[i].user_sp=allocPage(1);
            init_pcb_stack(pcb[i].kernel_sp,pcb[i].user_sp,lock_tasks[i-3]->entry_point,&pcb[i]);
            list_add_tail(&pcb[i].list, &ready_queue);
    }
	current_running=&pid0_pcb;//pid0_pcb不会执行，第一次运行do_scheduler时轮换到pcb[0]
    //mutex_lock->lock.status=UNLOCKED;
    /* remember to initialize `current_running`
     * TODO:
     */
}

static void init_syscall(void)
{
    // initialize system call table.
}

// jump from bootloader.
// The beginning of everything >_< ~~~~~~~~~~~~~~
int main()
{
    // init Process Control Block (-_-!)
    init_pcb();
    printk("> [INIT] PCB initialization succeeded.\n\r");
/*
    // read CPU frequency
    time_base = sbi_read_fdt(TIMEBASE);
	
    // init futex mechanism
    init_system_futex();

    // init interrupt (^_^)
    init_exception();
    printk("> [INIT] Interrupt processing initialization succeeded.\n\r");

    // init system call table (0_0)
    init_syscall();
    printk("> [INIT] System call initialized successfully.\n\r");

    // fdt_print(riscv_dtb);
*/
    // init screen (QAQ)
    init_screen();
    printk("> [INIT] SCREEN initialization succeeded.\n\r");

    // TODO:
    // Setup timer interrupt and enable all interrupt

    while (1) {
        // (QAQQQQQQQQQQQ)
        // If you do non-preemptive scheduling, you need to use it
        // to surrender control do_scheduler();
        // enable_interrupt();
        // __asm__ __volatile__("wfi\n\r":::);
        do_scheduler();
    };
    return 0;
}
